| Conditions | 1 |
| Paths | > 20000 |
| Total Lines | 811 |
| Code Lines | 517 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 0 | ||
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
| 1 | /** |
||
| 23 | }(function ($) { |
||
| 24 | 'use strict'; |
||
| 25 | |||
| 26 | var |
||
| 27 | utils = (function () { |
||
| 28 | return { |
||
| 29 | escapeRegExChars: function (value) { |
||
| 30 | return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); |
||
| 31 | }, |
||
| 32 | createNode: function (containerClass) { |
||
| 33 | var div = document.createElement('div'); |
||
| 34 | div.className = containerClass; |
||
| 35 | div.style.position = 'absolute'; |
||
| 36 | div.style.display = 'none'; |
||
| 37 | return div; |
||
| 38 | } |
||
| 39 | }; |
||
| 40 | }()), |
||
| 41 | |||
| 42 | keys = { |
||
| 43 | ESC: 27, |
||
| 44 | TAB: 9, |
||
| 45 | RETURN: 13, |
||
| 46 | LEFT: 37, |
||
| 47 | UP: 38, |
||
| 48 | RIGHT: 39, |
||
| 49 | DOWN: 40 |
||
| 50 | }; |
||
| 51 | |||
| 52 | function Autocomplete(el, options) { |
||
| 53 | var noop = function () { }, |
||
| 54 | that = this, |
||
| 55 | defaults = { |
||
| 56 | autoSelectFirst: false, |
||
| 57 | appendTo: 'body', |
||
| 58 | serviceUrl: null, |
||
| 59 | lookup: null, |
||
| 60 | onSelect: null, |
||
| 61 | width: 'auto', |
||
| 62 | minChars: 1, |
||
| 63 | maxHeight: 300, |
||
| 64 | deferRequestBy: 0, |
||
| 65 | params: {}, |
||
| 66 | formatResult: Autocomplete.formatResult, |
||
| 67 | delimiter: null, |
||
| 68 | zIndex: 9999, |
||
| 69 | type: 'GET', |
||
| 70 | noCache: false, |
||
| 71 | onSearchStart: noop, |
||
| 72 | onSearchComplete: noop, |
||
| 73 | onSearchError: noop, |
||
| 74 | containerClass: 'autocomplete-suggestions', |
||
| 75 | tabDisabled: false, |
||
| 76 | dataType: 'text', |
||
| 77 | currentRequest: null, |
||
| 78 | triggerSelectOnValidInput: true, |
||
| 79 | preventBadQueries: true, |
||
| 80 | lookupFilter: function (suggestion, originalQuery, queryLowerCase) { |
||
| 81 | return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1; |
||
| 82 | }, |
||
| 83 | paramName: 'query', |
||
| 84 | transformResult: function (response) { |
||
| 85 | return typeof response === 'string' ? $.parseJSON(response) : response; |
||
| 86 | } |
||
| 87 | }; |
||
| 88 | |||
| 89 | // Shared variables: |
||
| 90 | that.element = el; |
||
| 91 | that.el = $(el); |
||
| 92 | that.suggestions = []; |
||
| 93 | that.badQueries = []; |
||
| 94 | that.selectedIndex = -1; |
||
| 95 | that.currentValue = that.element.value; |
||
| 96 | that.intervalId = 0; |
||
| 97 | that.cachedResponse = {}; |
||
| 98 | that.onChangeInterval = null; |
||
| 99 | that.onChange = null; |
||
| 100 | that.isLocal = false; |
||
| 101 | that.suggestionsContainer = null; |
||
| 102 | that.options = $.extend({}, defaults, options); |
||
| 103 | that.classes = { |
||
| 104 | selected: 'autocomplete-selected', |
||
| 105 | suggestion: 'autocomplete-suggestion' |
||
| 106 | }; |
||
| 107 | that.hint = null; |
||
| 108 | that.hintValue = ''; |
||
| 109 | that.selection = null; |
||
| 110 | |||
| 111 | // Initialize and set options: |
||
| 112 | that.initialize(); |
||
| 113 | that.setOptions(options); |
||
| 114 | } |
||
| 115 | |||
| 116 | Autocomplete.utils = utils; |
||
| 117 | |||
| 118 | $.Autocomplete = Autocomplete; |
||
| 119 | |||
| 120 | Autocomplete.formatResult = function (suggestion, currentValue) { |
||
| 121 | var pattern = '(' + utils.escapeRegExChars(currentValue) + ')'; |
||
| 122 | |||
| 123 | return suggestion.value.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>'); |
||
| 124 | }; |
||
| 125 | |||
| 126 | Autocomplete.prototype = { |
||
| 127 | |||
| 128 | killerFn: null, |
||
| 129 | |||
| 130 | initialize: function () { |
||
| 131 | var that = this, |
||
| 132 | suggestionSelector = '.' + that.classes.suggestion, |
||
| 133 | selected = that.classes.selected, |
||
| 134 | options = that.options, |
||
| 135 | container; |
||
| 136 | |||
| 137 | // Remove autocomplete attribute to prevent native suggestions: |
||
| 138 | that.element.setAttribute('autocomplete', 'off'); |
||
| 139 | |||
| 140 | that.killerFn = function (e) { |
||
| 141 | if ($(e.target).closest('.' + that.options.containerClass).length === 0) { |
||
| 142 | that.killSuggestions(); |
||
| 143 | that.disableKillerFn(); |
||
| 144 | } |
||
| 145 | }; |
||
| 146 | |||
| 147 | that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass); |
||
| 148 | |||
| 149 | container = $(that.suggestionsContainer); |
||
| 150 | |||
| 151 | container.appendTo(options.appendTo); |
||
| 152 | |||
| 153 | // Only set width if it was provided: |
||
| 154 | if (options.width !== 'auto') { |
||
| 155 | container.width(options.width); |
||
| 156 | } |
||
| 157 | |||
| 158 | // Listen for mouse over event on suggestions list: |
||
| 159 | container.on('mouseover.autocomplete', suggestionSelector, function () { |
||
| 160 | that.activate($(this).data('index')); |
||
| 161 | }); |
||
| 162 | |||
| 163 | // Deselect Aktif element when mouse leaves suggestions container: |
||
| 164 | container.on('mouseout.autocomplete', function () { |
||
| 165 | that.selectedIndex = -1; |
||
| 166 | container.children('.' + selected).removeClass(selected); |
||
| 167 | }); |
||
| 168 | |||
| 169 | // Listen for click event on suggestions list: |
||
| 170 | container.on('click.autocomplete', suggestionSelector, function () { |
||
| 171 | that.select($(this).data('index')); |
||
| 172 | }); |
||
| 173 | |||
| 174 | that.fixPosition(); |
||
| 175 | |||
| 176 | that.fixPositionCapture = function () { |
||
| 177 | if (that.visible) { |
||
| 178 | that.fixPosition(); |
||
| 179 | } |
||
| 180 | }; |
||
| 181 | |||
| 182 | $(window).on('resize.autocomplete', that.fixPositionCapture); |
||
| 183 | |||
| 184 | that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); }); |
||
| 185 | that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); }); |
||
| 186 | that.el.on('blur.autocomplete', function () { that.onBlur(); }); |
||
| 187 | that.el.on('focus.autocomplete', function () { that.onFocus(); }); |
||
| 188 | that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); }); |
||
| 189 | }, |
||
| 190 | |||
| 191 | onFocus: function () { |
||
| 192 | var that = this; |
||
| 193 | that.fixPosition(); |
||
| 194 | if (that.options.minChars <= that.el.val().length) { |
||
| 195 | that.onValueChange(); |
||
| 196 | } |
||
| 197 | }, |
||
| 198 | |||
| 199 | onBlur: function () { |
||
| 200 | this.enableKillerFn(); |
||
| 201 | }, |
||
| 202 | |||
| 203 | setOptions: function (suppliedOptions) { |
||
| 204 | var that = this, |
||
| 205 | options = that.options; |
||
| 206 | |||
| 207 | $.extend(options, suppliedOptions); |
||
| 208 | |||
| 209 | that.isLocal = $.isArray(options.lookup); |
||
| 210 | |||
| 211 | if (that.isLocal) { |
||
| 212 | options.lookup = that.verifySuggestionsFormat(options.lookup); |
||
| 213 | } |
||
| 214 | |||
| 215 | // Adjust height, width and z-index: |
||
| 216 | $(that.suggestionsContainer).css({ |
||
| 217 | 'max-height': options.maxHeight + 'px', |
||
| 218 | 'width': options.width + 'px', |
||
| 219 | 'z-index': options.zIndex |
||
| 220 | }); |
||
| 221 | }, |
||
| 222 | |||
| 223 | clearCache: function () { |
||
| 224 | this.cachedResponse = {}; |
||
| 225 | this.badQueries = []; |
||
| 226 | }, |
||
| 227 | |||
| 228 | clear: function () { |
||
| 229 | this.clearCache(); |
||
| 230 | this.currentValue = ''; |
||
| 231 | this.suggestions = []; |
||
| 232 | }, |
||
| 233 | |||
| 234 | disable: function () { |
||
| 235 | var that = this; |
||
| 236 | that.disabled = true; |
||
| 237 | if (that.currentRequest) { |
||
| 238 | that.currentRequest.abort(); |
||
| 239 | } |
||
| 240 | }, |
||
| 241 | |||
| 242 | enable: function () { |
||
| 243 | this.disabled = false; |
||
| 244 | }, |
||
| 245 | |||
| 246 | fixPosition: function () { |
||
| 247 | var that = this, |
||
| 248 | offset, |
||
| 249 | styles; |
||
| 250 | |||
| 251 | // Don't adjsut position if custom container has been specified: |
||
| 252 | if (that.options.appendTo !== 'body') { |
||
| 253 | return; |
||
| 254 | } |
||
| 255 | |||
| 256 | offset = that.el.offset(); |
||
| 257 | |||
| 258 | styles = { |
||
| 259 | top: (offset.top + that.el.outerHeight()) + 'px', |
||
| 260 | left: offset.left + 'px' |
||
| 261 | }; |
||
| 262 | |||
| 263 | if (that.options.width === 'auto') { |
||
| 264 | styles.width = (that.el.outerWidth() - 2) + 'px'; |
||
| 265 | } |
||
| 266 | |||
| 267 | $(that.suggestionsContainer).css(styles); |
||
| 268 | }, |
||
| 269 | |||
| 270 | enableKillerFn: function () { |
||
| 271 | var that = this; |
||
| 272 | $(document).on('click.autocomplete', that.killerFn); |
||
| 273 | }, |
||
| 274 | |||
| 275 | disableKillerFn: function () { |
||
| 276 | var that = this; |
||
| 277 | $(document).off('click.autocomplete', that.killerFn); |
||
| 278 | }, |
||
| 279 | |||
| 280 | killSuggestions: function () { |
||
| 281 | var that = this; |
||
| 282 | that.stopKillSuggestions(); |
||
| 283 | that.intervalId = window.setInterval(function () { |
||
| 284 | that.hide(); |
||
| 285 | that.stopKillSuggestions(); |
||
| 286 | }, 50); |
||
| 287 | }, |
||
| 288 | |||
| 289 | stopKillSuggestions: function () { |
||
| 290 | window.clearInterval(this.intervalId); |
||
| 291 | }, |
||
| 292 | |||
| 293 | isCursorAtEnd: function () { |
||
| 294 | var that = this, |
||
| 295 | valLength = that.el.val().length, |
||
| 296 | selectionStart = that.element.selectionStart, |
||
| 297 | range; |
||
| 298 | |||
| 299 | if (typeof selectionStart === 'number') { |
||
| 300 | return selectionStart === valLength; |
||
| 301 | } |
||
| 302 | if (document.selection) { |
||
| 303 | range = document.selection.createRange(); |
||
| 304 | range.moveStart('character', -valLength); |
||
| 305 | return valLength === range.text.length; |
||
| 306 | } |
||
| 307 | return true; |
||
| 308 | }, |
||
| 309 | |||
| 310 | onKeyPress: function (e) { |
||
| 311 | var that = this; |
||
| 312 | |||
| 313 | // If suggestions are hidden and user presses arrow down, display suggestions: |
||
| 314 | if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) { |
||
| 315 | that.suggest(); |
||
| 316 | return; |
||
| 317 | } |
||
| 318 | |||
| 319 | if (that.disabled || !that.visible) { |
||
| 320 | return; |
||
| 321 | } |
||
| 322 | |||
| 323 | switch (e.which) { |
||
| 324 | case keys.ESC: |
||
| 325 | that.el.val(that.currentValue); |
||
| 326 | that.hide(); |
||
| 327 | break; |
||
| 328 | case keys.RIGHT: |
||
| 329 | if (that.hint && that.options.onHint && that.isCursorAtEnd()) { |
||
| 330 | that.selectHint(); |
||
| 331 | break; |
||
| 332 | } |
||
| 333 | return; |
||
| 334 | case keys.TAB: |
||
| 335 | if (that.hint && that.options.onHint) { |
||
| 336 | that.selectHint(); |
||
| 337 | return; |
||
| 338 | } |
||
|
|
|||
| 339 | // Fall through to RETURN |
||
| 340 | case keys.RETURN: |
||
| 341 | if (that.selectedIndex === -1) { |
||
| 342 | that.hide(); |
||
| 343 | return; |
||
| 344 | } |
||
| 345 | that.select(that.selectedIndex); |
||
| 346 | if (e.which === keys.TAB && that.options.tabDisabled === false) { |
||
| 347 | return; |
||
| 348 | } |
||
| 349 | break; |
||
| 350 | case keys.UP: |
||
| 351 | that.moveUp(); |
||
| 352 | break; |
||
| 353 | case keys.DOWN: |
||
| 354 | that.moveDown(); |
||
| 355 | break; |
||
| 356 | default: |
||
| 357 | return; |
||
| 358 | } |
||
| 359 | |||
| 360 | // Cancel event if function did not return: |
||
| 361 | e.stopImmediatePropagation(); |
||
| 362 | e.preventDefault(); |
||
| 363 | }, |
||
| 364 | |||
| 365 | onKeyUp: function (e) { |
||
| 366 | var that = this; |
||
| 367 | |||
| 368 | if (that.disabled) { |
||
| 369 | return; |
||
| 370 | } |
||
| 371 | |||
| 372 | switch (e.which) { |
||
| 373 | case keys.UP: |
||
| 374 | case keys.DOWN: |
||
| 375 | return; |
||
| 376 | } |
||
| 377 | |||
| 378 | clearInterval(that.onChangeInterval); |
||
| 379 | |||
| 380 | if (that.currentValue !== that.el.val()) { |
||
| 381 | that.findBestHint(); |
||
| 382 | if (that.options.deferRequestBy > 0) { |
||
| 383 | // Defer lookup in case when value changes very quickly: |
||
| 384 | that.onChangeInterval = setInterval(function () { |
||
| 385 | that.onValueChange(); |
||
| 386 | }, that.options.deferRequestBy); |
||
| 387 | } else { |
||
| 388 | that.onValueChange(); |
||
| 389 | } |
||
| 390 | } |
||
| 391 | }, |
||
| 392 | |||
| 393 | onValueChange: function () { |
||
| 394 | var that = this, |
||
| 395 | options = that.options, |
||
| 396 | value = that.el.val(), |
||
| 397 | query = that.getQuery(value), |
||
| 398 | index; |
||
| 399 | |||
| 400 | if (that.selection) { |
||
| 401 | that.selection = null; |
||
| 402 | (options.onInvalidateSelection || $.noop).call(that.element); |
||
| 403 | } |
||
| 404 | |||
| 405 | clearInterval(that.onChangeInterval); |
||
| 406 | that.currentValue = value; |
||
| 407 | that.selectedIndex = -1; |
||
| 408 | |||
| 409 | // Check existing suggestion for the match before proceeding: |
||
| 410 | if (options.triggerSelectOnValidInput) { |
||
| 411 | index = that.findSuggestionIndex(query); |
||
| 412 | if (index !== -1) { |
||
| 413 | that.select(index); |
||
| 414 | return; |
||
| 415 | } |
||
| 416 | } |
||
| 417 | |||
| 418 | if (query.length < options.minChars) { |
||
| 419 | that.hide(); |
||
| 420 | } else { |
||
| 421 | that.getSuggestions(query); |
||
| 422 | } |
||
| 423 | }, |
||
| 424 | |||
| 425 | findSuggestionIndex: function (query) { |
||
| 426 | var that = this, |
||
| 427 | index = -1, |
||
| 428 | queryLowerCase = query.toLowerCase(); |
||
| 429 | |||
| 430 | $.each(that.suggestions, function (i, suggestion) { |
||
| 431 | if (suggestion.value.toLowerCase() === queryLowerCase) { |
||
| 432 | index = i; |
||
| 433 | return false; |
||
| 434 | } |
||
| 435 | }); |
||
| 436 | |||
| 437 | return index; |
||
| 438 | }, |
||
| 439 | |||
| 440 | getQuery: function (value) { |
||
| 441 | var delimiter = this.options.delimiter, |
||
| 442 | parts; |
||
| 443 | |||
| 444 | if (!delimiter) { |
||
| 445 | return value; |
||
| 446 | } |
||
| 447 | parts = value.split(delimiter); |
||
| 448 | return $.trim(parts[parts.length - 1]); |
||
| 449 | }, |
||
| 450 | |||
| 451 | getSuggestionsLocal: function (query) { |
||
| 452 | var that = this, |
||
| 453 | options = that.options, |
||
| 454 | queryLowerCase = query.toLowerCase(), |
||
| 455 | filter = options.lookupFilter, |
||
| 456 | limit = parseInt(options.lookupLimit, 10), |
||
| 457 | data; |
||
| 458 | |||
| 459 | data = { |
||
| 460 | suggestions: $.grep(options.lookup, function (suggestion) { |
||
| 461 | return filter(suggestion, query, queryLowerCase); |
||
| 462 | }) |
||
| 463 | }; |
||
| 464 | |||
| 465 | if (limit && data.suggestions.length > limit) { |
||
| 466 | data.suggestions = data.suggestions.slice(0, limit); |
||
| 467 | } |
||
| 468 | |||
| 469 | return data; |
||
| 470 | }, |
||
| 471 | |||
| 472 | getSuggestions: function (q) { |
||
| 473 | var response, |
||
| 474 | that = this, |
||
| 475 | options = that.options, |
||
| 476 | serviceUrl = options.serviceUrl, |
||
| 477 | params, |
||
| 478 | cacheKey; |
||
| 479 | |||
| 480 | options.params[options.paramName] = q; |
||
| 481 | params = options.ignoreParams ? null : options.params; |
||
| 482 | |||
| 483 | if (that.isLocal) { |
||
| 484 | response = that.getSuggestionsLocal(q); |
||
| 485 | } else { |
||
| 486 | if ($.isFunction(serviceUrl)) { |
||
| 487 | serviceUrl = serviceUrl.call(that.element, q); |
||
| 488 | } |
||
| 489 | cacheKey = serviceUrl + '?' + $.param(params || {}); |
||
| 490 | response = that.cachedResponse[cacheKey]; |
||
| 491 | } |
||
| 492 | |||
| 493 | if (response && $.isArray(response.suggestions)) { |
||
| 494 | that.suggestions = response.suggestions; |
||
| 495 | that.suggest(); |
||
| 496 | } else if (!that.isBadQuery(q)) { |
||
| 497 | if (options.onSearchStart.call(that.element, options.params) === false) { |
||
| 498 | return; |
||
| 499 | } |
||
| 500 | if (that.currentRequest) { |
||
| 501 | that.currentRequest.abort(); |
||
| 502 | } |
||
| 503 | that.currentRequest = $.ajax({ |
||
| 504 | url: serviceUrl, |
||
| 505 | data: params, |
||
| 506 | type: options.type, |
||
| 507 | dataType: options.dataType |
||
| 508 | }).done(function (data) { |
||
| 509 | var result; |
||
| 510 | that.currentRequest = null; |
||
| 511 | result = options.transformResult(data); |
||
| 512 | that.processResponse(result, q, cacheKey); |
||
| 513 | options.onSearchComplete.call(that.element, q, result.suggestions); |
||
| 514 | }).fail(function (jqXHR, textStatus, errorThrown) { |
||
| 515 | options.onSearchError.call(that.element, q, jqXHR, textStatus, errorThrown); |
||
| 516 | }); |
||
| 517 | } |
||
| 518 | }, |
||
| 519 | |||
| 520 | isBadQuery: function (q) { |
||
| 521 | if (!this.options.preventBadQueries){ |
||
| 522 | return false; |
||
| 523 | } |
||
| 524 | |||
| 525 | var badQueries = this.badQueries, |
||
| 526 | i = badQueries.length; |
||
| 527 | |||
| 528 | while (i--) { |
||
| 529 | if (q.indexOf(badQueries[i]) === 0) { |
||
| 530 | return true; |
||
| 531 | } |
||
| 532 | } |
||
| 533 | |||
| 534 | return false; |
||
| 535 | }, |
||
| 536 | |||
| 537 | hide: function () { |
||
| 538 | var that = this; |
||
| 539 | that.visible = false; |
||
| 540 | that.selectedIndex = -1; |
||
| 541 | $(that.suggestionsContainer).hide(); |
||
| 542 | that.signalHint(null); |
||
| 543 | }, |
||
| 544 | |||
| 545 | suggest: function () { |
||
| 546 | if (this.suggestions.length === 0) { |
||
| 547 | this.hide(); |
||
| 548 | return; |
||
| 549 | } |
||
| 550 | |||
| 551 | var that = this, |
||
| 552 | options = that.options, |
||
| 553 | formatResult = options.formatResult, |
||
| 554 | value = that.getQuery(that.currentValue), |
||
| 555 | className = that.classes.suggestion, |
||
| 556 | classSelected = that.classes.selected, |
||
| 557 | container = $(that.suggestionsContainer), |
||
| 558 | beforeRender = options.beforeRender, |
||
| 559 | html = '', |
||
| 560 | index, |
||
| 561 | width; |
||
| 562 | |||
| 563 | if (options.triggerSelectOnValidInput) { |
||
| 564 | index = that.findSuggestionIndex(value); |
||
| 565 | if (index !== -1) { |
||
| 566 | that.select(index); |
||
| 567 | return; |
||
| 568 | } |
||
| 569 | } |
||
| 570 | |||
| 571 | // Build suggestions inner HTML: |
||
| 572 | $.each(that.suggestions, function (i, suggestion) { |
||
| 573 | html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value) + '</div>'; |
||
| 574 | }); |
||
| 575 | |||
| 576 | // If width is auto, adjust width before displaying suggestions, |
||
| 577 | // because if instance was created before input had width, it will be zero. |
||
| 578 | // Also it adjusts if input width has changed. |
||
| 579 | // -2px to account for suggestions border. |
||
| 580 | if (options.width === 'auto') { |
||
| 581 | width = that.el.outerWidth() - 2; |
||
| 582 | container.width(width > 0 ? width : 300); |
||
| 583 | } |
||
| 584 | |||
| 585 | container.html(html); |
||
| 586 | |||
| 587 | // Select first value by default: |
||
| 588 | if (options.autoSelectFirst) { |
||
| 589 | that.selectedIndex = 0; |
||
| 590 | container.children().first().addClass(classSelected); |
||
| 591 | } |
||
| 592 | |||
| 593 | if ($.isFunction(beforeRender)) { |
||
| 594 | beforeRender.call(that.element, container); |
||
| 595 | } |
||
| 596 | |||
| 597 | container.show(); |
||
| 598 | that.visible = true; |
||
| 599 | |||
| 600 | that.findBestHint(); |
||
| 601 | }, |
||
| 602 | |||
| 603 | findBestHint: function () { |
||
| 604 | var that = this, |
||
| 605 | value = that.el.val().toLowerCase(), |
||
| 606 | bestMatch = null; |
||
| 607 | |||
| 608 | if (!value) { |
||
| 609 | return; |
||
| 610 | } |
||
| 611 | |||
| 612 | $.each(that.suggestions, function (i, suggestion) { |
||
| 613 | var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0; |
||
| 614 | if (foundMatch) { |
||
| 615 | bestMatch = suggestion; |
||
| 616 | } |
||
| 617 | return !foundMatch; |
||
| 618 | }); |
||
| 619 | |||
| 620 | that.signalHint(bestMatch); |
||
| 621 | }, |
||
| 622 | |||
| 623 | signalHint: function (suggestion) { |
||
| 624 | var hintValue = '', |
||
| 625 | that = this; |
||
| 626 | if (suggestion) { |
||
| 627 | hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length); |
||
| 628 | } |
||
| 629 | if (that.hintValue !== hintValue) { |
||
| 630 | that.hintValue = hintValue; |
||
| 631 | that.hint = suggestion; |
||
| 632 | (this.options.onHint || $.noop)(hintValue); |
||
| 633 | } |
||
| 634 | }, |
||
| 635 | |||
| 636 | verifySuggestionsFormat: function (suggestions) { |
||
| 637 | // If suggestions is string array, convert them to supported format: |
||
| 638 | if (suggestions.length && typeof suggestions[0] === 'string') { |
||
| 639 | return $.map(suggestions, function (value) { |
||
| 640 | return { value: value, data: null }; |
||
| 641 | }); |
||
| 642 | } |
||
| 643 | |||
| 644 | return suggestions; |
||
| 645 | }, |
||
| 646 | |||
| 647 | processResponse: function (result, originalQuery, cacheKey) { |
||
| 648 | var that = this, |
||
| 649 | options = that.options; |
||
| 650 | |||
| 651 | result.suggestions = that.verifySuggestionsFormat(result.suggestions); |
||
| 652 | |||
| 653 | // Cache results if cache is not disabled: |
||
| 654 | if (!options.noCache) { |
||
| 655 | that.cachedResponse[cacheKey] = result; |
||
| 656 | if (options.preventBadQueries && result.suggestions.length === 0) { |
||
| 657 | that.badQueries.push(originalQuery); |
||
| 658 | } |
||
| 659 | } |
||
| 660 | |||
| 661 | // Return if originalQuery is not matching current query: |
||
| 662 | if (originalQuery !== that.getQuery(that.currentValue)) { |
||
| 663 | return; |
||
| 664 | } |
||
| 665 | |||
| 666 | that.suggestions = result.suggestions; |
||
| 667 | that.suggest(); |
||
| 668 | }, |
||
| 669 | |||
| 670 | activate: function (index) { |
||
| 671 | var that = this, |
||
| 672 | AktifItem, |
||
| 673 | selected = that.classes.selected, |
||
| 674 | container = $(that.suggestionsContainer), |
||
| 675 | children = container.children(); |
||
| 676 | |||
| 677 | container.children('.' + selected).removeClass(selected); |
||
| 678 | |||
| 679 | that.selectedIndex = index; |
||
| 680 | |||
| 681 | if (that.selectedIndex !== -1 && children.length > that.selectedIndex) { |
||
| 682 | AktifItem = children.get(that.selectedIndex); |
||
| 683 | $(AktifItem).addClass(selected); |
||
| 684 | return AktifItem; |
||
| 685 | } |
||
| 686 | |||
| 687 | return null; |
||
| 688 | }, |
||
| 689 | |||
| 690 | selectHint: function () { |
||
| 691 | var that = this, |
||
| 692 | i = $.inArray(that.hint, that.suggestions); |
||
| 693 | |||
| 694 | that.select(i); |
||
| 695 | }, |
||
| 696 | |||
| 697 | select: function (i) { |
||
| 698 | var that = this; |
||
| 699 | that.hide(); |
||
| 700 | that.onSelect(i); |
||
| 701 | }, |
||
| 702 | |||
| 703 | moveUp: function () { |
||
| 704 | var that = this; |
||
| 705 | |||
| 706 | if (that.selectedIndex === -1) { |
||
| 707 | return; |
||
| 708 | } |
||
| 709 | |||
| 710 | if (that.selectedIndex === 0) { |
||
| 711 | $(that.suggestionsContainer).children().first().removeClass(that.classes.selected); |
||
| 712 | that.selectedIndex = -1; |
||
| 713 | that.el.val(that.currentValue); |
||
| 714 | that.findBestHint(); |
||
| 715 | return; |
||
| 716 | } |
||
| 717 | |||
| 718 | that.adjustScroll(that.selectedIndex - 1); |
||
| 719 | }, |
||
| 720 | |||
| 721 | moveDown: function () { |
||
| 722 | var that = this; |
||
| 723 | |||
| 724 | if (that.selectedIndex === (that.suggestions.length - 1)) { |
||
| 725 | return; |
||
| 726 | } |
||
| 727 | |||
| 728 | that.adjustScroll(that.selectedIndex + 1); |
||
| 729 | }, |
||
| 730 | |||
| 731 | adjustScroll: function (index) { |
||
| 732 | var that = this, |
||
| 733 | AktifItem = that.activate(index), |
||
| 734 | offsetTop, |
||
| 735 | upperBound, |
||
| 736 | lowerBound, |
||
| 737 | heightDelta = 25; |
||
| 738 | |||
| 739 | if (!AktifItem) { |
||
| 740 | return; |
||
| 741 | } |
||
| 742 | |||
| 743 | offsetTop = AktifItem.offsetTop; |
||
| 744 | upperBound = $(that.suggestionsContainer).scrollTop(); |
||
| 745 | lowerBound = upperBound + that.options.maxHeight - heightDelta; |
||
| 746 | |||
| 747 | if (offsetTop < upperBound) { |
||
| 748 | $(that.suggestionsContainer).scrollTop(offsetTop); |
||
| 749 | } else if (offsetTop > lowerBound) { |
||
| 750 | $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta); |
||
| 751 | } |
||
| 752 | |||
| 753 | that.el.val(that.getValue(that.suggestions[index].value)); |
||
| 754 | that.signalHint(null); |
||
| 755 | }, |
||
| 756 | |||
| 757 | onSelect: function (index) { |
||
| 758 | var that = this, |
||
| 759 | onSelectCallback = that.options.onSelect, |
||
| 760 | suggestion = that.suggestions[index]; |
||
| 761 | |||
| 762 | that.currentValue = that.getValue(suggestion.value); |
||
| 763 | |||
| 764 | if (that.currentValue !== that.el.val()) { |
||
| 765 | that.el.val(that.currentValue); |
||
| 766 | } |
||
| 767 | |||
| 768 | that.signalHint(null); |
||
| 769 | that.suggestions = []; |
||
| 770 | that.selection = suggestion; |
||
| 771 | |||
| 772 | if ($.isFunction(onSelectCallback)) { |
||
| 773 | onSelectCallback.call(that.element, suggestion); |
||
| 774 | } |
||
| 775 | }, |
||
| 776 | |||
| 777 | getValue: function (value) { |
||
| 778 | var that = this, |
||
| 779 | delimiter = that.options.delimiter, |
||
| 780 | currentValue, |
||
| 781 | parts; |
||
| 782 | |||
| 783 | if (!delimiter) { |
||
| 784 | return value; |
||
| 785 | } |
||
| 786 | |||
| 787 | currentValue = that.currentValue; |
||
| 788 | parts = currentValue.split(delimiter); |
||
| 789 | |||
| 790 | if (parts.length === 1) { |
||
| 791 | return value; |
||
| 792 | } |
||
| 793 | |||
| 794 | return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value; |
||
| 795 | }, |
||
| 796 | |||
| 797 | dispose: function () { |
||
| 798 | var that = this; |
||
| 799 | that.el.off('.autocomplete').removeData('autocomplete'); |
||
| 800 | that.disableKillerFn(); |
||
| 801 | $(window).off('resize.autocomplete', that.fixPositionCapture); |
||
| 802 | $(that.suggestionsContainer).remove(); |
||
| 803 | } |
||
| 804 | }; |
||
| 805 | |||
| 806 | // Create chainable jQuery plugin: |
||
| 807 | $.fn.autocomplete = function (options, args) { |
||
| 808 | var dataKey = 'autocomplete'; |
||
| 809 | // If function invoked without argument return |
||
| 810 | // instance of the first matched element: |
||
| 811 | if (arguments.length === 0) { |
||
| 812 | return this.first().data(dataKey); |
||
| 813 | } |
||
| 814 | |||
| 815 | return this.each(function () { |
||
| 816 | var inputElement = $(this), |
||
| 817 | instance = inputElement.data(dataKey); |
||
| 818 | |||
| 819 | if (typeof options === 'string') { |
||
| 820 | if (instance && typeof instance[options] === 'function') { |
||
| 821 | instance[options](args); |
||
| 822 | } |
||
| 823 | } else { |
||
| 824 | // If instance already exists, destroy it: |
||
| 825 | if (instance && instance.dispose) { |
||
| 826 | instance.dispose(); |
||
| 827 | } |
||
| 828 | instance = new Autocomplete(this, options); |
||
| 829 | inputElement.data(dataKey, instance); |
||
| 830 | } |
||
| 831 | }); |
||
| 832 | }; |
||
| 833 | })); |
||
| 834 |